(function(){

    /**
     * @class Bible.CrudPlugin
     * @extends Object
     */
	var CrudPlugin = Ext.extend(Object, {
        /**
         * @cfg {Ext.grid.GridPanel} grid
         */
        /**
         * @cfg {Ext.form.FormPanel} form
         */
        /**
         * @cfg {Ext.Button} newButton
         */
        /**
         * @cfg {Ext.Button} saveButton
         */
        /**
         * @cfg {Ext.Button} cancelButton
         */
        disableNewClickEvent: false,
        /**
         * @cfg {Ext.Button} deleteButton optional
         */
        /**
         * @cfg {Boolean} pauseChangeEvents stop onChange function when loading/clearing form
         * (defaults to <tt>true</tt>)
         */
        pauseChangeEvents: false,

        /**
         * @cfg {Boolean} initOnlyCrudFields Defaults to false.
         */
        initOnlyCrudFields: false,

        isNew: false,

		constructor: function(config){
			Ext.apply(this, config);
		},

		init: function(comp){
            var me = this;
            me.comp = comp;

            Ext.applyIf(me.form, {
                setValues: function(value){
                    return me.setValues(value);
                },
                getOriginalValue: function(){
                    return me.originalValue;
                },
                getPauseChangeEvents: function(){
                    console.debug('this.comp.getPauseChangeEvents');
                    return me.getPauseChangeEvents();
                },
                setPauseChangeEvents: function(pause){
                    console.debug('this.comp.setPauseChangeEvents');
                    return me.setPauseChangeEvents(pause);
                },
                showMask: function(){
                    this.setDisabled(true);
                },
                hideMask: function(){
                    this.setDisabled(false);
                }
            });

            me.comp.addEvents(
                /**
                 * @event validate
                 */
                'validate',
                /**
                 * @event itemselect
                 */
                'itemselect'
                );

			me.initEvents();

            me.changeButtonsState(false, true);
		},

        // private
        initEvents: function(){
            var me = this;

            //console.debug('initEvents');
            me.grid.getSelectionModel().on({
                scope: me,
				clearselections: me.onRowDeselect,
                rowselect: me.onRowSelect
			});

            me.form.on({
                scope: me,
                afterrender: me.formAfterRender
            });

			me.saveButton.on('click', me.onSave, me);
            me.cancelButton.on('click', me.onCancel, me);

            if (!me.disableNewClickEvent){
			    me.newButton.on('click', me.onNew, me);
            }
            if(me.deleteButton){
                me.deleteButton.on('click', me.onDelete, me);
                me.deleteButton.setDisabled(true); // initial disable
            }

            me.comp.onCancelButton = function(){
                me.onCancel();
            };
            me.comp.onNewButton = function(){
                me.onNew();
            };
            me.comp.isComponentValid = function(){
                me.isValid();
            };

            me.initItemsEvents(me.form.items);
            // verify if form is crudField
            if(me.initOnlyCrudFields && me.form.crudField){
                me.addItemEvents(me.form);
            }
        },

        /**
         * shortcut, fire event for this.component
         */
        fireEvent: function(){
            return this.comp.fireEvent.apply(this.comp, arguments);
        },

        formAfterRender: function(){
            var me = this;
            me.initValue();
            //console.debug('form afterrender. selections: ', this.grid.getSelectionModel().hasSelection(), ', isNew: ', this.isNew);
            if(!me.isNew && !me.grid.getSelectionModel().hasSelection()){
                me.form.setDisabled(true);
            }
        },

        // private
		initItemsEvents: function(items){
            //console.debug('initItemsEvents');
			items.each(function(item){
                if (item.isComposite || item.getXType() == 'fieldset'){
                    if (Ext.isArray(item.items)){
                        for(var i = 0, len = item.items.length; i < len; i++){
                            this.initItemEvents(item.items[i]);
                        }
                    } else {
                        //mixed collection
                        item.items.each(function(item){
                            this.initItemEvents(item);
                        }, this)
                    }
                } else {
                    this.initItemEvents(item);
                }
			}, this);
		},

        // private
        initItemEvents: function(item){
//            console.debug('initItemEvents', item.name);
            if (item.items){
                if(item.crudField){
                    this.addItemEvents(item);
                }
                this.initItemsEvents(item.items);
            } else if(item.crudField || (!this.initOnlyCrudFields && Ext.form.FormPanel.prototype.isField(item))){
                this.addItemEvents(item);
            }
        },

        // private
        addItemEvents: function(item){
            var me = this;
            item.on('keyup', me.onChange, me);
            item.on('change', me.onChange, me);
            //if(item.getXType() == 'combo'){
                item.on("select", me.onChange, me);
            //}
            if(item.getXType() == 'checkbox'){
                item.on("check", me.onChange, me);
            }
            if(item.getXType() == 'spinnerfield'){
                item.on("spin", me.onChange, me);
            }
        },

        // private
		onRowSelect: function(sm, index, r){
            var me = this;
            //console.debug('onRowSelect');
            me.setIsNew(false);
            me.form.setDisabled(false);
            me.changeButtonsState(false, true);
            if(me.deleteButton){
                me.deleteButton.setDisabled(false);
            }

            //this.setPauseChangeEvents(true);
            // TODO use 2 lines to setValues on form
            //this.fireEvent('itemselect', sm, index, r);
            // must call form.setValues(values) in on.itemselect ;
            //this.setPauseChangeEvents(false);
		},

        // private
        setValues: function(value){
            console.debug('Crud: setValues:', value);
            if(this.form.getForm){
                this.form.getForm().setValues(value);
            }
            this.initValue(value);
        },

		onRowDeselect: function(sm, index, r){
            //console.debug('onRowDeselect');
            var me = this;
            if(!me.isNew){
                me.form.setDisabled(true);
            }
            me.doReset();
            if(me.deleteButton){
                me.deleteButton.setDisabled(true);
            }
		},

        doReset: function(){
            if(this.form.getForm){
                this.form.getForm().reset();
            }
        },

        onSave: function(item, response){
            var me = this;
            me.setIsNew(false);
            me.changeButtonsState(false, true);
            me.grid.setDisabled(false);
            me.initValue();
        },

        /**
         * @param {Boolean} isNew
         */
        setIsNew: function(isNew){
            var me = this;
            me.isNew = isNew;
            me.comp.isNew = isNew;
            me.form.isNew = isNew;
            me.newButton.setDisabled(isNew);
        },

        onNew: function(){
            var me = this;
            console.debug('onNew');
			me.setIsNew(true);

			me.grid.getSelectionModel().clearSelections();
            me.grid.setDisabled(true);

			me.changeButtonsState(true, false);
            me.form.setDisabled(false);
            me.fireEvent('newitem');
		},

        onDelete: function(){
            console.debug('TODO onDelete');
        },

		onCancel: function(){
            //console.debug('onCancel');
			var me = this,
                record,
                isNew = me.isNew;

            if (isNew){
                console.debug('onCancel new object');
                me.doReset();
                me.form.setDisabled(true);
                me.grid.setDisabled(false);
            } else if(record = me.grid.getSelectionModel().getSelected()){
                me.setValues(me.originalValue);
            }
            me.setIsNew(false);
            me.changeButtonsState(false, true);
		},

        /**
         * @return {Boolean} pauseChangeEvents
         */
        getPauseChangeEvents: function(){
            return this.pauseChangeEvents;
        },

        /**
         * @param {Boolean} pause
         */
        setPauseChangeEvents: function(pause){
            this.pauseChangeEvents = pause;
        },

		isChanged: function(){
            if (!this.grid.getSelectionModel().getSelected()){
                //console.debug('isChanged (not selected)', false);
                return false;
            }

            var changes = !Bible.deepEquals(this.getValues(), this.originalValue);
            console.debug('isChanged', changes);
            //console.dir(this.originalValue);
            //console.dir(this.getValues());
			return changes;
		},

        /**
         * @param {Boolean} changes
         * @param {Boolean} valid
         */
        changeButtonsState: function(changes, valid){
            console.debug('changeButtonsState(changes, valid)',changes, valid);
            this.saveButton.setDisabled(!valid || !changes);
            this.cancelButton.setDisabled(!changes);
        },

        onChange: function(){
            this.fireEvent("formchanged");
//            console.debug('onChange');
            if(!this.getPauseChangeEvents()){
                this.adjustButtons();
            }
        },

		adjustButtons: function(){
            console.debug('adjustButtons');
			var valid,
                changed;

            changed = this.isNew || this.isChanged();
            if (changed){
                valid = this.isValid();
            }

            this.changeButtonsState(changed, valid);
		},

        isValid: function(){
            //console.debug('isValid');
            var valid = false;

            if (this.form.getForm){
                valid = this.form.getForm().isValid();
            } else {
                valid = true; // otherwise valid will remain always false
            }

            valid = valid && (this.fireEvent("validate") !== false);
            //console.debug('isValid', valid);
            return valid;
        },

        initValue: function(value){
            var formValue = this.getValues();
            /**
             * @type {Object}
             * @property originalValue
             */
            //this.originalValue = Bible.deepCloneObject(formValue);
            //TODO must use values for better perf - see why deepClone?
            //this.originalValue = value || this.getValues();
            this.originalValue = this.getValues();
        },

        getValues: function(){
            //return this.form.getForm().getFieldValues();
            return this.form.getForm().getValues();
        }
	});

	Bible.CrudPlugin = CrudPlugin;
})();